package com.hospital.wx.service.Impl;

import com.baidu.aip.contentcensor.AipContentCensor;
import com.baidu.aip.speech.AipSpeech;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hospital.wx.config.BaiduSpeechConfig;
import com.hospital.wx.pojo.SpeechRecognitionResult;
import com.hospital.wx.service.ChatService;
import com.hospital.wx.util.validator.SensitiveWordsValidator;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static java.util.ResourceBundle.clearCache;

/**
 * 聊天服务实现类
 * 处理与智谱AI的交互和内容审核
 * 包含以下功能：
 * 1. Redis缓存机制：缓存AI回答，避免重复调用
 * 2. 相似度匹配：查找相似问题，复用已有回答
 * 3. 问题分类：按类别缓存问题，提高匹配效率
 * 4. 热门问题：统计高频问题，优先使用缓存
 * 5. 敏感词过滤：确保回答内容合规
 */
@Slf4j
@Service
public class ChatServiceImpl implements ChatService {

    @Value("${zhipu.api.key}")
    private String apiKey;

    @Value("${zhipu.api.url}")
    private String apiUrl;

    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final StringRedisTemplate redisTemplate;
    private final SensitiveWordsValidator sensitiveWordsValidator;

    // Redis缓存相关常量
    private static final String CACHE_KEY_PREFIX = "chat:qa:";
    private static final String CATEGORY_CACHE_PREFIX = "chat:category:";   //对话类型
    private static final String HOT_QUESTIONS_KEY = "chat:hot:questions";   //对话热请求
    private static final long CACHE_EXPIRE_TIME = 24 * 60 * 60;  // 24小时过期
    private static final int MAX_CACHE_SIZE = 10000;  // 最大缓存条数
    private static final int MAX_HOT_QUESTIONS = 100;  // 最大热门问题数
    private static final double SIMILARITY_THRESHOLD = 0.8;  // 相似度阈值

    @Value("${baidu.shenhe.appId}")
    private String APP_ID;

    @Value("${baidu.shenhe.api-key}")
    private String API_KEY;

    @Value("${baidu.shenhe.secret-key}")
    private String SECRET_KEY;

    @Autowired
    private BaiduSpeechConfig speechConfig;

    private AipSpeech client;   //百度智能云的工具

    private AipContentCensor aipContentCensor;



    /**
     * 构造函数
     * @param restTemplate REST请求模板
     * @param objectMapper JSON对象映射器
     * @param redisTemplate Redis模板
     * @param sensitiveWordsValidator 敏感词验证器
     */
    public ChatServiceImpl(RestTemplate restTemplate, ObjectMapper objectMapper,
                           StringRedisTemplate redisTemplate, SensitiveWordsValidator sensitiveWordsValidator) {
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
        this.redisTemplate = redisTemplate;
        this.sensitiveWordsValidator = sensitiveWordsValidator;
    }

    /**
     * 获取AI响应
     * 按照以下优先级获取回答：
     * 1. 热门问题缓存
     * 2. 相似问题匹配
     * 3. 分类缓存
     * 4. 直接缓存
     * 5. 调用AI接口
     * @param userMessage 用户消息
     * @return AI响应内容
     */
    @Override
    public String getAIResponse(String userMessage) {
        try {
            // 1. 检查是否是热门问题
            String hotAnswer = getHotAnswer(userMessage);
            if (hotAnswer != null) {
                // 验证缓存的回答是否合规
                if (!sensitiveWordsValidator.isValid(hotAnswer, null)) {
                    log.warn("缓存的热门回答包含敏感内容，清除缓存");
                    clearCache(userMessage);
                } else {
                    log.debug("从热门问题缓存中获取到回答");
                    return hotAnswer;
                }
            }

            // 2. 检查是否有相似问题
            String similarAnswer = findSimilarQuestion(userMessage);
            if (similarAnswer != null) {
                // 验证缓存的回答是否合规
                if (!sensitiveWordsValidator.isValid(similarAnswer, null)) {
                    log.warn("缓存的相似问题回答包含敏感内容，清除缓存");
                    clearCache(userMessage);
                } else {
                    log.debug("从相似问题中获取到回答");
                    return similarAnswer;
                }
            }

            // 3. 检查分类缓存
            String categoryAnswer = getCachedAnswer(userMessage);
            if (categoryAnswer != null) {
                // 验证缓存的回答是否合规
                if (!sensitiveWordsValidator.isValid(categoryAnswer, null)) {
                    log.warn("缓存的分类回答包含敏感内容，清除缓存");
                    clearCache(userMessage);
                } else {
                    log.debug("从分类缓存中获取到回答");
                    return categoryAnswer;
                }
            }

            // 4. 检查直接缓存
            String cachedResponse = redisTemplate.opsForValue().get(CACHE_KEY_PREFIX + userMessage);
            if (cachedResponse != null) {
                // 验证缓存的回答是否合规
                if (!sensitiveWordsValidator.isValid(cachedResponse, null)) {
                    log.warn("缓存的回答包含敏感内容，清除缓存");
                    clearCache(userMessage);
                } else {
                    log.debug("从直接缓存中获取到回答");
                    return cachedResponse;
                }
            }

            // 5. 调用AI接口获取新回答
            String aiResponse = callAI(userMessage);

            // 6. 验证AI响应是否合规
            if (!sensitiveWordsValidator.isValid(aiResponse, null)) {
                log.warn("AI响应内容审核未通过: {}", sensitiveWordsValidator.getErrorMessage());
                return "抱歉，由于以下原因，我们无法提供相关建议：\n" +
                        sensitiveWordsValidator.getErrorMessage() +
                        "\n请咨询专业医生获取帮助。";
            }

            // 7. 只有合规的回答才缓存
            saveToCache(userMessage, aiResponse);
            updateHotQuestions(userMessage);

            return aiResponse;
        } catch (Exception e) {
            log.error("获取AI响应失败", e);
            throw new RuntimeException("获取AI响应失败", e);
        }
    }

    /**
     * 调用智谱AI接口
     * @param userMessage 用户消息
     * @return AI响应内容
     */
    private String callAI(String userMessage) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + apiKey);
        headers.set("Accept", "application/json");

        Map<String, Object> messageBody = new HashMap<>();
        messageBody.put("model", "glm-4");  // 确认使用正确的模型名称
        messageBody.put("stream", false);    // 是否使用流式响应
        messageBody.put("temperature", 0.7);
        messageBody.put("top_p", 0.7);      // 添加top_p参数
        messageBody.put("max_tokens", 1000);

        List<Map<String, String>> messages = new ArrayList<>();
        messages.add(Map.of(
                "role", "system",
                "content", "你是一个专业的医疗助手，请根据用户的症状描述提供专业的医疗建议。注意：你的建议仅供参考，重要医疗决策请咨询专业医生。"
        ));
        messages.add(Map.of(
                "role", "user",
                "content", userMessage
        ));

        messageBody.put("messages", messages);
        messageBody.put("temperature", 0.7);  // 控制回答的随机性
        messageBody.put("max_tokens", 1000);  // 限制回答的最大长度

        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(messageBody, headers);

        log.debug("发送请求到智谱AI");
        try {
            ResponseEntity<Map> response = restTemplate.exchange(
                    apiUrl,
                    HttpMethod.POST,
                    entity,
                    Map.class
            );

        /*
         * 抑制编译器产生的特定类型的警告信息。
         *第一次使用 @SuppressWarnings("unchecked")：
            response.getBody().get("choices") 返回的是 Object 类型。
            你把它强制转换为 List<Map<String, Object>> 类型。由于编译器无法在运行时保证这个转换是安全的，所以会发出 unchecked 警告。通过添加 @SuppressWarnings("unchecked") 注解，你告诉编译器忽略这个警告。
            第二次使用 @SuppressWarnings("unchecked")：
            choices.get(0).get("message") 返回的也是 Object 类型。
            你把它强制转换为 Map<String, String> 类型。同样，编译器无法在运行时确定这个转换是否安全，会发出 unchecked 警告，使用该注解来抑制这个警告。
        */
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> choices = (List<Map<String, Object>>) response.getBody().get("choices");
                if (!choices.isEmpty()) {
                    @SuppressWarnings("unchecked")
                    Map<String, String> message = (Map<String, String>) choices.get(0).get("message");
                    String aiResponse = message.get("content");

                    // 使用注解验证器进行内容审核
                    if (!sensitiveWordsValidator.isValid(aiResponse, null)) {
                        log.warn("AI响应内容审核未通过: {}", sensitiveWordsValidator.getErrorMessage());
                        aiResponse = "抱歉，由于以下原因，我们无法提供相关建议：\n" +
                                sensitiveWordsValidator.getErrorMessage() +
                                "\n请咨询专业医生获取帮助。";
                    }

                    return aiResponse;
                }
            } else {
                log.error("AI服务响应异常: status={}, body={}",
                        response.getStatusCode(),
                        response.getBody());
                throw new RuntimeException("AI服务响应异常: " + response.getStatusCode());

            }
        } catch (Exception e) {
            log.error("调用AI服务失败: {}", e.getMessage(), e);
            throw new RuntimeException("调用AI服务失败: " + e.getMessage());
        }
        throw new RuntimeException("Failed to get response from AI service");
    }

    /**
     * 保存回答到Redis缓存
     * 包含缓存大小限制和过期时间设置
     * @param question 问题
     * @param answer 回答
     */
    private void saveToCache(String question, String answer) {
        // 检查缓存大小
        Long size = redisTemplate.opsForValue().size(CACHE_KEY_PREFIX + "*");
        if (size != null && size >= MAX_CACHE_SIZE) {
            // 如果超过最大缓存数，删除最早的缓存
            String oldestKey = getOldestCacheKey();
            if (oldestKey != null) {
                redisTemplate.delete(oldestKey);
            }
        }

        // 存储新的缓存
        redisTemplate.opsForValue().set(
                CACHE_KEY_PREFIX + question,
                answer,
                CACHE_EXPIRE_TIME,
                TimeUnit.SECONDS
        );
        log.debug("将回答存入缓存");
    }

    /**
     * 获取最早的缓存键
     * @return 最早的缓存键
     */
    private String getOldestCacheKey() {
        Set<String> keys = redisTemplate.keys(CACHE_KEY_PREFIX + "*");
        if (keys != null && !keys.isEmpty()) {
            return keys.iterator().next();
        }
        return null;
    }

    /**
     * 查找相似问题
     * 使用编辑距离算法计算相似度
     * @param currentQuestion 当前问题
     * @return 相似问题的回答
     */
    private String findSimilarQuestion(String currentQuestion) {
        Set<String> keys = redisTemplate.keys(CACHE_KEY_PREFIX + "*");
        if (keys != null) {
            for (String key : keys) {
                String cachedQuestion = key.substring(CACHE_KEY_PREFIX.length());
                if (calculateSimilarity(currentQuestion, cachedQuestion) > SIMILARITY_THRESHOLD) {
                    return redisTemplate.opsForValue().get(key);
                }
            }
        }
        return null;
    }

    /**
     * 计算两个字符串的相似度
     * 使用编辑距离算法
     * @param s1 字符串1
     * @param s2 字符串2
     * @return 相似度（0-1之间）
     */
    private double calculateSimilarity(String s1, String s2) {
        int distance = levenshteinDistance(s1, s2);
        int maxLength = Math.max(s1.length(), s2.length());
        return 1.0 - ((double) distance / maxLength);
    }

    /**
     * 计算编辑距离
     * @param s1 字符串1
     * @param s2 字符串2
     * @return 编辑距离
     */
    private int levenshteinDistance(String s1, String s2) {
        int[][] dp = new int[s1.length() + 1][s2.length() + 1];
        for (int i = 0; i <= s1.length(); i++) {
            dp[i][0] = i;
        }
        for (int j = 0; j <= s2.length(); j++) {
            dp[0][j] = j;
        }
        for (int i = 1; i <= s1.length(); i++) {
            for (int j = 1; j <= s2.length(); j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
                }
            }
        }
        return dp[s1.length()][s2.length()];
    }

    /**
     * 获取问题类别
     * @param question 问题内容
     * @return 问题类别
     */
    private String getCategory(String question) {
        if (question.contains("感冒") || question.contains("发烧")) {
            return "respiratory";
        } else if (question.contains("胃") || question.contains("消化")) {
            return "digestive";
        } else if (question.contains("心") || question.contains("血压")) {
            return "cardiovascular";
        } else if (question.contains("皮肤")) {
            return "dermatology";
        } else if (question.contains("眼") || question.contains("耳") || question.contains("鼻")) {
            return "ent";
        }
        return "general";
    }

    /**
     * 从分类缓存中获取回答
     * @param question 问题
     * @return 缓存的回答
     */
    private String getCachedAnswer(String question) {
        String category = getCategory(question);
        String categoryKey = CATEGORY_CACHE_PREFIX + category;

        Set<String> keys = redisTemplate.keys(categoryKey + ":*");
        if (keys != null) {
            for (String key : keys) {
                String cachedQuestion = key.substring(categoryKey.length() + 1);
                if (calculateSimilarity(question, cachedQuestion) > SIMILARITY_THRESHOLD) {
                    return redisTemplate.opsForValue().get(key);
                }
            }
        }
        return null;
    }

    /**
     * 更新热门问题统计
     * @param question 问题
     */
    private void updateHotQuestions(String question) {
        redisTemplate.opsForZSet().incrementScore(HOT_QUESTIONS_KEY, question, 1);
        redisTemplate.opsForZSet().removeRange(HOT_QUESTIONS_KEY, 0, -MAX_HOT_QUESTIONS-1);
    }

    /**
     * 从热门问题中获取回答
     * @param question 问题
     * @return 缓存的回答
     */
    private String getHotAnswer(String question) {
        Double score = redisTemplate.opsForZSet().score(HOT_QUESTIONS_KEY, question);
        if (score != null && score > 10) {  // 如果问题被问过10次以上
            return redisTemplate.opsForValue().get(CACHE_KEY_PREFIX + question);
        }
        return null;
    }

    /**
     * 清除指定问题的缓存
     * @param question 问题
     */
    private void clearCache(String question) {
        // 清除直接缓存
        String directKey = CACHE_KEY_PREFIX + question;
        redisTemplate.delete(directKey);

        // 清除分类缓存
        String category = getCategory(question);
        String categoryKey = CATEGORY_CACHE_PREFIX + category;
        Set<String> categoryKeys = redisTemplate.keys(categoryKey + ":*");
        if (categoryKeys != null && !categoryKeys.isEmpty()) {
            redisTemplate.delete(categoryKeys);
        }

        // 清除热门问题缓存
        redisTemplate.opsForZSet().remove(HOT_QUESTIONS_KEY, question);

        log.debug("已清除问题相关缓存: {}", question);
    }


    /*
     * 定义用于百度智能云的初始化工具
     * */
    @PostConstruct
    public void init() {
        client = new AipSpeech(speechConfig.getAppId(),
                speechConfig.getApiKey(),
                speechConfig.getSecretKey());
    }

    /*调用语音API接口进行识别*/
    @Override
    public SpeechRecognitionResult recognizeSpeech(MultipartFile audioFile) throws Exception {
        //将multipartFile转换为字节数组
        byte[] audioData = audioFile.getBytes();

        //调用百度语音识别API
        JSONObject result = client.asr(audioData, "pcm", 16000, null);

        //检查语音识别结果
        if (result.getInt("err_no") != 0) {
            log.error("语音识别失败：{}", result.toString());
            throw new RuntimeException("语音识别失败：" + result.getString("err_msg"));
        }

        String recognizeText = result.getJSONArray("result").getString(0);

        //初始化AipContentCensor
        aipContentCensor = new AipContentCensor(APP_ID, API_KEY, SECRET_KEY);

        //内容审核
        JSONObject censorResult = aipContentCensor.textCensorUserDefined(recognizeText);

        //处理审核结果
        SpeechRecognitionResult response = new SpeechRecognitionResult();
        response.setText(recognizeText);

        if (censorResult.has("conclusionType")) {
            if (censorResult.getInt("conclusionType") != 1) {  //1表示通过
                response.setStatus("REJECTED");

                //获取其违规原因
                if (censorResult.has("data")) {
                    JSONArray data = censorResult.getJSONArray("data");
                    List<String> violationReasons = new ArrayList<>();

                    for (int i = 0; i < data.length(); i++) {
                        JSONObject reason = data.getJSONObject(i);
                        violationReasons.add(reason.getString("msg"));
                    }

                    response.setRejectReason(String.join(", ", violationReasons));
                    log.warn("内容审核未通过：{}", response);
                }
            } else {
                response.setStatus("APPROVED");
            }
        } else {
            log.error("内容审核结果中缺少 conclusionType 字段：{}", censorResult.toString());
            response.setStatus("ERROR");
            response.setRejectReason("内容审核结果格式异常");
        }

        return response;
    }


}